package com.remoter.api.util;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.remoter.api.context.IModule;

public class RemoterServiceStatus {
	
	private static final ConcurrentMap<String,RemoterServiceStatus> SERVICE_STATISTICS = new ConcurrentHashMap<String,RemoterServiceStatus>();
	private static final ConcurrentMap<String,ConcurrentMap<String,RemoterServiceStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String,RemoterServiceStatus>>();
	
	private final ConcurrentMap<String, Object> values = new ConcurrentHashMap<String, Object>();
    private final AtomicInteger active = new AtomicInteger();
    private final AtomicLong total = new AtomicLong();
    private final AtomicInteger failed = new AtomicInteger();
    private final AtomicLong totalElapsed = new AtomicLong();
    private final AtomicLong failedElapsed = new AtomicLong();
    private final AtomicLong maxElapsed = new AtomicLong();
    private final AtomicLong failedMaxElapsed = new AtomicLong();
    private final AtomicLong succeededMaxElapsed = new AtomicLong();
    
    private RemoterServiceStatus() {}
	
	public static RemoterServiceStatus getStatus(IModule module){
		String key = module.getServiceKey();
		RemoterServiceStatus status = SERVICE_STATISTICS.get(key);
		if(null == status){
			SERVICE_STATISTICS.putIfAbsent(key,new RemoterServiceStatus());
			status = SERVICE_STATISTICS.get(key);
		}
		return status;
	}
	
	public static void removeStatus(IModule module){
		SERVICE_STATISTICS.remove(module.getServiceKey());
	}
	
	public static RemoterServiceStatus getStatus(IModule module,String methodName){
		String key = module.getServiceKey();
		ConcurrentMap<String,RemoterServiceStatus> map = METHOD_STATISTICS.get(key);
		if(null == map){
			METHOD_STATISTICS.putIfAbsent(key,new ConcurrentHashMap<String,RemoterServiceStatus>());
			map = METHOD_STATISTICS.get(key);
		}
		RemoterServiceStatus status = map.get(methodName);
		if(null == status){
			map.putIfAbsent(methodName,new RemoterServiceStatus());
			status = map.get(methodName);
		}
		return status;
	}
	
	public static void removeStatus(IModule module,String methodName){
		String key = module.getServiceKey();
		ConcurrentMap<String,RemoterServiceStatus> map = METHOD_STATISTICS.get(key);
        if(map != null){
            map.remove(methodName);
        }
	}
	
	public static void beginCount(IModule module,String methodName){
		beginCount(getStatus(module));
		beginCount(getStatus(module,methodName));
	}
	
	private static void beginCount(RemoterServiceStatus status){
		status.active.incrementAndGet();
	}
	
	public static void endCount(IModule module, String methodName, long elapsed, boolean succeeded){
		endCount(getStatus(module),elapsed,succeeded);
        endCount(getStatus(module,methodName),elapsed,succeeded);
	}
	
	private static void endCount(RemoterServiceStatus status, long elapsed, boolean succeeded){
		status.active.decrementAndGet();
        status.total.incrementAndGet();
        status.totalElapsed.addAndGet(elapsed);
        if(status.maxElapsed.get() < elapsed){
        	status.maxElapsed.set(elapsed);
        }
        if(succeeded){
        	if(status.succeededMaxElapsed.get() < elapsed){
        		status.succeededMaxElapsed.set(elapsed);
        	}
        }else{
        	status.failed.incrementAndGet();
            status.failedElapsed.addAndGet(elapsed);
            if(status.failedMaxElapsed.get() < elapsed){
            	status.failedMaxElapsed.set(elapsed);
            }
        }
	}
	
	public void set(String key,Object value){
        values.put(key,value);
    }
	
	public Object get(String key){
        return values.get(key);
    }
	
	public int getActive(){
        return active.get();
    }
	
	public long getTotal(){
        return total.longValue();
    }
	
	public long getTotalElapsed(){
        return totalElapsed.get();
    }
	
	public long getAverageElapsed(){
        long total = getTotal();
        if(total == 0){
            return 0;
        }
        return getTotalElapsed() / total;
    }
	
	public long getMaxElapsed(){
        return maxElapsed.get();
    }
	
	public int getFailed(){
        return failed.get();
    }
	
	public long getFailedElapsed(){
        return failedElapsed.get();
    }
	
	public long getFailedAverageElapsed(){
        long failed = getFailed();
        if(failed == 0){
            return 0;
        }
        return getFailedElapsed() / failed;
    }
	
	public long getFailedMaxElapsed(){
        return failedMaxElapsed.get();
    }
	
	public long getSucceeded(){
        return getTotal() - getFailed();
    }
	
	public long getSucceededElapsed(){
        return getTotalElapsed() - getFailedElapsed();
    }
	
	public long getSucceededAverageElapsed(){
        long succeeded = getSucceeded();
        if(succeeded == 0){
            return 0;
        }
        return getSucceededElapsed() / succeeded;
    }
	
	public long getSucceededMaxElapsed(){
        return succeededMaxElapsed.get();
    }
	
	public long getAverageTps(){
        if(getTotalElapsed() >= 1000L){
            return getTotal() / (getTotalElapsed() / 1000L);
        }
        return getTotal();
    }
	
}